<?php

class InstallController extends AppController
{
	public $helpers = array('Html', 'Form');

	public $uses = array();

	public $autoRender = false;

	/**
	 * @todo Remove once authentication is working.
	 */
	public function beforeFilter()
	{
		parent::beforeFilter();
		$this->Auth->allowedActions = array('*');
	}

	public function index()
	{
		if (!empty($this->params['form']))
		{
			$task = low($this->params['form']['task']);
			if ($task == 'install' || $task == 'upgrade')
			{
				$this->setAction($task);
			}
		}
		else
		{
			$this->render(null, null, VIEWS . low($this->name) . DS . 'index.ctp');
		}
	}

	public function install()
	{
		if ($this->verifyPrerequisites())
		{
			$this->createDatabaseTables();
			$this->addDefaultGroups();
			$this->addDefaultAdministrator();
			$this->buildAccessControlList();
			$this->setControllerPermissions();
			$this->render(null, null, VIEWS . low($this->name) . DS . 'success.ctp');
		}
	}

	public function upgrade()
	{
	}

	private function verifyPrerequisites()
	{
		$notWriteableDirectories = $this->testForWritableDirectories(array(TMP));
		if (! empty($notWriteableDirectories))
		{
			$this->set(compact('notWriteableDirectories'));
			$this->render(null, null, VIEWS . low($this->name) . DS . 'file_system.ctp');
			return false;
		}

		return true;;
	}

	private function testForWritableDirectories($writableDirectories)
	{
		$notWriteableDirectories = array();

		foreach ($writableDirectories as $directory)
		{
			if (!is_writable($directory))
			{
				$notWriteableDirectories[] = $directory;
			}
		}

		return $notWriteableDirectories;
	}

	private function createDatabaseTables()
	{
		App::Import('ConnectionManager');

		$db =& ConnectionManager::getDataSource('default');
		if (! $db->isConnected())
		{
			$this->render(null, null, VIEWS . low($this->name) . DS . 'error.ctp');
			exit();
		}

		$sqlFile = CONFIGS . 'sql' . DS . 'otime.' . $db->config['driver'] . '.sql';

        $contents = file_get_contents($sqlFile);

		$comment_patterns = array('/\/\*.*(\n)*.*(\*\/)?/', // C comments
								  '/\s*--.*\n/',            // inline comments start with --
								  '/\s*#.*\n/',             // inline comments start with #
								  );

		$contents = preg_replace($comment_patterns, "\n", $contents);

		$statements = explode(";\n", $contents);
		$statements = preg_replace("/\s/", ' ', $statements);

        foreach ($statements as $statement)
		{
            if (trim($statement) != '')
			{
                $db->query($statement);
            }
        }

		// TODO: Verify that the database tables have been added correctly.
	}

	private function addDefaultGroups()
	{
		$this->loadModel('Group');

		foreach ($this->Group->getDefaultGroups() as $group)
		{
			$this->Group->create();
			$this->Group->set(array('groupname' => $group));
			$this->Group->save();
		}
	}

	private function addDefaultAdministrator()
	{
		$this->loadModel('User');

		$this->User->create();

		$this->User->set(array(
					'username' => 'admin',
					'password' => Security::hash('admin', null, true),
					'is_active' => '1',
					'group_id' => '1'
					));

		$this->User->save();
	}

	/**
	 * Rebuild the Acl based on the current controllers in the application
	 *
	 * @return void
	 */
	private function buildAccessControlList()
	{
		$log = array();

		$aco =& ClassRegistry::init('Aco');
		$root = $aco->node('controllers');

		if (! $root)
		{
			$aco->create(array('parent_id' => null, 'model' => null, 'alias' => 'controllers'));
			$root = $aco->save();
			$root['Aco']['id'] = $aco->id;
			$log[] = 'Created Aco node for controllers';
		}
		else
		{
			$root = $root[0];
		}

		App::import('Core', 'File');
		$controllers = Configure::listObjects('controller');
		$appIndex = array_search('App', $controllers);

		if ($appIndex !== false )
		{
			unset($controllers[$appIndex]);
		}

		$baseMethods = get_class_methods('Controller');
		$baseMethods[] = 'buildAcl';

		// Add the controllers of plugins
		$plugins = $this->getPluginControllerNames();
		$controllers = array_merge($controllers, $plugins);

		// Look at each controller in app/controllers
		foreach ($controllers as $ctrlName)
		{
			// TODO: Replace with Reflection

			App::import('Controller', $ctrlName);
			$ctrlclass = $ctrlName . 'Controller';
			$methods = get_class_methods($ctrlclass);

			// Find / make controller node
			$controllerNode = $aco->node('controllers/' . $ctrlName);
			if (! $controllerNode)
			{
				$aco->create(array('parent_id' => $root['Aco']['id'], 'model' => null, 'alias' => $ctrlName));
				$controllerNode = $aco->save();
				$controllerNode['Aco']['id'] = $aco->id;
				$log[] = 'Created Aco node for ' . $ctrlName;
			}
			else
			{
				$controllerNode = $controllerNode[0];
			}

			// Clean the methods. to remove those in Controller and private actions.
			foreach ($methods as $k => $method)
			{
				if (strpos($method, '_', 0) === 0)
				{
					unset($methods[$k]);
					continue;
				}

				if (in_array($method, $baseMethods)) {
					unset($methods[$k]);
					continue;
				}

				$methodNode = $aco->node('controllers/' . $ctrlName . '/' . $method);
				if (! $methodNode)
				{
					$aco->create(array('parent_id' => $controllerNode['Aco']['id'], 'model' => null, 'alias' => $method));
					$methodNode = $aco->save();
					$log[] = 'Created Aco node for ' . $method;
				}
			}
		}

		debug($log);
	}


	/* Get the names of the plugin controllers
	 *
	 * This function will get an array of the plugin controller names, and
	 * also makes sure the controllers are available for us to get the
	 * method names by doing an App::import for each plugin controller.
	 *
	 * @return array List of plugin names
	 */
	private function getPluginControllerNames()
	{
		App::import('Core', 'File', 'Folder');

		$paths = Configure::getInstance();
		$folder =& new Folder();

		// Change directory to the plugins
		$folder->cd(APP . 'plugins');

		// Get a list of the files that have a file name that ends with controller.php
		$files = $folder->findRecursive('.*_controller\.php');

		// Get the list of plugins
		$plugins = Configure::listObjects('plugin');

		// Loop through the controllers we found in the plugins directory
		foreach ($files as $f => $fileName)
		{
			// Get the base file name
			$file = basename($fileName);

			// Get the controller name
			$file = Inflector::camelize(substr($file, 0, strlen($file) - strlen('_controller.php')));

			// Loop through the plugins
			foreach ($plugins as $pluginName)
			{
				if (preg_match('/^' . $pluginName . '/', $file))
				{
					// First, get rid of the App controller for the plugin
					// We do this because the app controller is never called directly
					if (preg_match('/^' . $pluginName . 'App/', $file))
					{
						unset($files[$f]);
					}
					else
					{
						if (! App::import('Controller', $pluginName . '.' . $file))
						{
							debug('Error importing ' . $file . ' for plugin ' . $pluginName);
						}

						// Now prepend the Plugin name
						// This is required to allow us to fetch the method names
						$files[$f] = $file;
					}

					break;
				}
			}
		}

		return $files;
	}

	private function setControllerPermissions()
	{
		App::import('Component', 'Acl');
		App::import('Model', 'DbAcl');

		$this->Acl = new AclComponent();
		$controller = null;
		$this->Acl->startup($controller);

		$group =& $this->User->Group;

		// Allow admins to everything
		$group->id = 1;
		$this->Acl->allow($group, 'controllers');

		// Allow managers to projects
		$group->id = 2;
		$this->Acl->deny($group, 'controllers');
		$this->Acl->allow($group, 'controllers/Users/index');
		$this->Acl->allow($group, 'controllers/Projects');

		// Allow users to only add and edit on posts and widgets
		$group->id = 3;
		$this->Acl->deny($group, 'controllers');
		$this->Acl->allow($group, 'controllers/Users/index');
	}
}

?>
